#include "EventLoop.h"
#include <mswsock.h>
#include <assert.h>
#include "DSTH_IOCP.h"
#include "Timestamp.h"
#include "IOCPOperation.h"
#include "TcpSocketOps.h"
#include "ChannelBuffer.h"
#include "Options.h"
#include "Log.h"

namespace dsth {

EventLoop::EventLoop() 
    : looping_(false),
    stop_(true),
    proactor_(new DSTHIocp) {
}

EventLoop::~EventLoop() {

}

void EventLoop::Loop() {
    assert(!looping_);
    if (looping_) {
        return;
    }
    stop_ = false;
    looping_ = true;
    int timeout = GetOption("ReactorTimeoutUsec").intVal;
    while (!stop_) {
        Timestamp recv_time = proactor_->Run(timeout);
        // TODO: other task
    }
}

void EventLoop::Quit() {
    stop_ = true;
    //TODO: close me
}

void EventLoop::RegisterHandle(HANDLE handle) {
    proactor_->RegisterHandleOrDie(handle);
}

void EventLoop::AsyncAccept(SOCKET accept_socket, IOCPAcceptOperation* op) {
    DWORD bytes_read = 0;
    op->peer_socket_ = net::CreateWSASocket();
    if (op->peer_socket_ == INVALID_SOCKET) {
        proactor_->OnCompletion(op, ::WSAGetLastError());
    }
    else {
        BOOL result = ::AcceptEx(accept_socket, op->peer_socket_, op->outbuffer_,
            0, sizeof (sockaddr_in) + 16, sizeof (sockaddr_in) + 16, &bytes_read, op);
        DWORD last_error = ::WSAGetLastError();
        if (!result && last_error != WSA_IO_PENDING) {
            proactor_->OnCompletion(op, last_error);
        }
        else {
            proactor_->OnPending(op);
        }
    }
}

void EventLoop::AsyncRead(ChannelBuffer* buffer, IOCPWaiteOperation* op) {
    uint64_t offset = 0;
    DWORD bytes_transferred = 0;
    op->Offset = offset & 0xFFFFFFFF;
    op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
    BOOL ok = ::ReadFile((HANDLE)op->peer_socket_,
        buffer->GetWriter(),
        buffer->WritableBytes(),
        &bytes_transferred, op);
    DWORD last_error = ::GetLastError();
    if (!ok && last_error != ERROR_IO_PENDING
        && last_error != ERROR_MORE_DATA) {
        proactor_->OnCompletion(op, last_error);
    }
    else if (ok) {
        printf("bytes_transferred %d\n", bytes_transferred);
        buffer->HasWritten(bytes_transferred);
        proactor_->OnCompletion(op, 0, bytes_transferred);
    }

    // Wait for the operation to complete.
    //DWORD bytes_transferred = 0;
    //ok = ::GetOverlappedResult(op->peer_socket_,
    //    op, &bytes_transferred, TRUE);
    //if (!ok) {
    //    last_error = ::GetLastError();
    //    proactor_->OnCompletion(op, last_error);
    //}
}

void EventLoop::AsyncWrite(const char* data, size_t len, IOCPWaiteOperation* op) {
    DWORD bytes_transferred = 0;
    uint64_t offset = 0;
    op->Offset = offset & 0xFFFFFFFF;
    op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
    BOOL ok = ::WriteFile((HANDLE)op->peer_socket_,
        data,
        len,
        &bytes_transferred, op);
    DWORD last_error = ::GetLastError();
    if (!ok && last_error != ERROR_IO_PENDING
        && last_error != ERROR_MORE_DATA)
    {
        proactor_->OnCompletion(op, last_error, bytes_transferred);
    }
    else
    {
        // TODO:pending me
        if (bytes_transferred == len) {
            proactor_->OnCompletion(op, 0, bytes_transferred);
        }
        else {
            printf("fixme ???\n");
        }
    }
}

void EventLoop::RunInLoop(const EventTask& task) { task(); }
bool EventLoop::IsInLoopThread() { return true; }

} // namespace
